# Automatically run `cargo update` periodically

---
name: Bump dependencies in Cargo.lock
on:
  schedule:
    # Run weekly
    - cron: '0 0 * * Sun'
  workflow_dispatch:
    # Needed so we can run it manually
permissions:
  contents: read
defaults:
  run:
    shell: bash
env:
  # So cargo doesn't complain about unstable features
  RUSTC_BOOTSTRAP: 1
  PR_TITLE: Weekly `cargo update`
  PR_MESSAGE: |
    Automation to keep dependencies in `Cargo.lock` current.

    The following is the output from `cargo update`:
  COMMIT_MESSAGE: "cargo update \n\n"

jobs:
  not-waiting-on-bors:
    if: github.repository_owner == 'rust-lang'
    name: skip if S-waiting-on-bors
    runs-on: ubuntu-latest
    steps:
      - env:
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
        run: |
          # Fetch state and labels of PR
          # Or exit successfully if PR does not exist
          JSON=$(gh pr view cargo_update --repo $GITHUB_REPOSITORY --json labels,state || exit 0)
          STATE=$(echo "$JSON" | jq -r '.state')
          WAITING_ON_BORS=$(echo "$JSON" | jq '.labels[] | any(.name == "S-waiting-on-bors"; .)')

          # Exit with error if open and S-waiting-on-bors
          if [[ "$STATE" == "OPEN" && "$WAITING_ON_BORS" == "true" ]]; then
            exit 1
          fi

  update:
    if: github.repository_owner == 'rust-lang'
    name: update dependencies
    needs: not-waiting-on-bors
    runs-on: ubuntu-latest
    steps:
      - name: checkout the source code
        uses: actions/checkout@v4
        with:
          submodules: recursive
      - name: install the bootstrap toolchain
        run: |
          # Extract the stage0 version
          TOOLCHAIN=$(jq -r '.compiler | {version,date} | join("-")' -- src/stage0.json)
          # Install and set as default
          rustup toolchain install --no-self-update --profile minimal $TOOLCHAIN
          rustup default $TOOLCHAIN

      - name: cargo update
        # Remove first line that always just says "Updating crates.io index"
        run: cargo update 2>&1 | sed '/crates.io index/d' | tee -a cargo_update.log
      - name: upload Cargo.lock artifact for use in PR
        uses: actions/upload-artifact@v3
        with:
          name: Cargo-lock
          path: Cargo.lock
          retention-days: 1
      - name: upload cargo-update log artifact for use in PR
        uses: actions/upload-artifact@v3
        with:
          name: cargo-updates
          path: cargo_update.log
          retention-days: 1

  pr:
    if: github.repository_owner == 'rust-lang'
    name: amend PR
    needs: update
    runs-on: ubuntu-latest
    permissions:
      contents: write
      pull-requests: write
    steps:
      - name: checkout the source code
        uses: actions/checkout@v4

      - name: download Cargo.lock from update job
        uses: actions/download-artifact@v3
        with:
          name: Cargo-lock
      - name: download cargo-update log from update job
        uses: actions/download-artifact@v3
        with:
          name: cargo-updates

      - name: craft PR body and commit message
        run: |
          echo "${COMMIT_MESSAGE}" > commit.txt
          cat cargo_update.log >> commit.txt

          echo "${PR_MESSAGE}" > body.md
          echo '```txt' >> body.md
          cat cargo_update.log >> body.md
          echo '```' >> body.md

      - name: commit
        run: |
          git config user.name github-actions
          git config user.email github-actions@github.com
          git switch --force-create cargo_update
          git add ./Cargo.lock
          git commit --no-verify --file=commit.txt

      - name: push
        run: git push --no-verify --force --set-upstream origin cargo_update

      - name: edit existing open pull request
        id: edit
        # Don't fail job if we need to open new PR
        continue-on-error: true
        env:
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
        run: |
          # Exit with error if PR is closed
          STATE=$(gh pr view cargo_update --repo $GITHUB_REPOSITORY --json state --jq '.state')
          if [[ "$STATE" != "OPEN" ]]; then
            exit 1
          fi

          gh pr edit cargo_update --title "${PR_TITLE}" --body-file body.md --repo $GITHUB_REPOSITORY

      - name: open new pull request
        # Only run if there wasn't an existing PR
        if: steps.edit.outcome != 'success'
        env:
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
        run: gh pr create --title "${PR_TITLE}" --body-file body.md --repo $GITHUB_REPOSITORY
